package main

import "fmt"
import "reflect"

type Student struct {
	Name 	string `json:"name" form:"username"`
	Age  	int    `json:"age"`
	Score int    `json:"score"`
}

func (s Student) GetInfo() string {
	return fmt.Sprintf("name: %s, age: %d, score: %d\n", s.Name, s.Age, s.Score)
}

func (s *Student) SetName(name string) {
	s.Name = name
}

func (s *Student) SetAge(age int) {
	s.Age = age
}

func (s *Student) SetScore(score int) {
	s.Score = score
}

// 打印字段
func PrintStructField(x interface{}) {
	t := reflect.TypeOf(x)
	fmt.Println(t)
	k := t.Kind()
	fmt.Println(k)

	// 这种方式也可以获取类型
	// v := reflect.ValueOf(x)
	// fmt.Println(v)
	// k1 := v.Kind()
	// fmt.Println(k1)

	// 先确定传入的是一个struct
	if k != reflect.Struct {
		fmt.Println("expect struct")
		return
	}

	// 1、通过类型变量中的Field可以获取结构体的字段（通过index）
	field0 := t.Field(0)
	// 这里t.Field(n)返回的就是一个结构体
	fmt.Printf("%#v\n", field0)
	// reflect.StructField{Name:"Name", PkgPath:"", Type:(*reflect.rtype)(0x101d7c0), Tag:"json:\"name\"", Offset:0x0, Index:[]int{0}, Anonymous:false}
	fmt.Println("字段名称：", field0.Name)
	// 字段名称： Name
	fmt.Println("字段类型：", field0.Type)
	// 字段类型： string
	fmt.Println("字段标签：", field0.Tag)
	// 字段标签： json:"name"
	// 注意这里一个字段是会有多个tag的，tag本身就是一个key-value形式，field0.Tag获取的是全部tag
	// 如果我们定义字段时是这样：Name 	string `json:"name" form:"username"`，那么此时Name字段就有两个tag，分别是json和form
	// 如果我们想获取某个字段的某个tag，可以这样写：
	fmt.Println("字段标签json：", field0.Tag.Get("json"))
	// 字段标签json： name
	// 获取Name字段的form标签
	fmt.Println("Name字段的form标签：", field0.Tag.Get("form"))
	// Name字段的form标签： username

	// 2、通过类型变量中的FieldByName可以获取结构体的字段（通过字段名称获取）
	field1, ok := t.FieldByName("Name")
	if ok {
		fmt.Printf("%#v\n", field1)
		// reflect.StructField{Name:"Name", PkgPath:"", Type:(*reflect.rtype)(0xc6f9e0), Tag:"json:\"name\" form:\"username\"", Offset:0x0, Index:[]int{0}, Anonymous:false}
	} else {
		fmt.Println("字段不存在")
	}

	// 3、通过类型变量中的NumField获取结构体有几个字段
	var fieldCount = t.NumField()
	fmt.Println("结构体有几个字段？有", fieldCount, "个")
	// 结构体有几个字段？有 3 个

	// 4、通过值变量获取结构体属性对应的值
	v := reflect.ValueOf(x)
	var name = v.FieldByName("Name")
	fmt.Println("Name对应的值：", name)
	// Name对应的值： Tom
	var age = v.FieldByName("Age")
	fmt.Println("Age对应的值：", age)
	// Age对应的值： 18
}

func main() {
	s := &Student{Name: "Tom", Age: 18, Score: 0}
	// fmt.Println(s.GetInfo())
	// s.SetAge(20)
	// fmt.Println(s.GetInfo())
	PrintStructField(*s)
}